/**
 * package Environnement
 */
package ca.csf.dfc.poo.bots;

import java.util.HashMap;
import java.util.Random;

import ca.csf.dfc.poo.env.Case;
import ca.csf.dfc.poo.env.Direction;
import ca.csf.dfc.poo.env.Environnement;
import ca.csf.dfc.poo.env.Position;

/**
 * Classe qui étend la classe Robot représentant un objet Robot intelligent qui cherche la récompense la plus payante à
 * long terme.
 * 
 * @author Camille Besse
 */
public class RobotIntelligent extends Robot
{
    /**
     * Tableau des Q-valeurs du robot intelligent.
     */
    private Qvalues[][] m_Q;

    /**
     * Mémoriation de la direction choisie pour la mise à jour
     */
    private Direction m_decisionPrise;

    /**
     * Constructeur d'initialisation.
     * Initialise un objet Robot intelligent avec une position aléatoire.
     * 
     * @param p_Environnement 
     * 				Environnement dans lequel se trouve le robot
     * @param p_EnvLargeur 
     * 				Largeur de l'environnement
     * @param p_EnvHauteur 
     * 				Hauteur de l'environnement
     * @param p_r 
     * 				Générateur de nombre aléatoires
     */
    public RobotIntelligent(Environnement p_Environnement, int p_EnvLargeur, int p_EnvHauteur, Random p_r)
    {
        super(p_r, p_Environnement);
        this.m_Q = new Qvalues[p_EnvHauteur][p_EnvLargeur];

        for (int y = 0; y < this.m_Q[0].length; y++)
        {
            for (int x = 0; x < this.m_Q.length; x++)
            {
                this.m_Q[x][y] = new Qvalues();
            }
        }
    }
    
    /**
     * Constructeur d'initialisation. 
     * Initialise un objet Robot intelligent avec la position donnée en paramètre.
     * 
     * @param p_position
     *            : Position initiale
     * @param p_EnvLargeur
     *            : Largeur de l'environnement
     * @param p_EnvHauteur
     *            : Hauteur de l'environnement
     */
    public RobotIntelligent(Position p_position, int p_EnvLargeur, int p_EnvHauteur, Random p_r)
    {
        super(p_position, p_r);
        this.m_Q = new Qvalues[p_EnvHauteur][p_EnvLargeur];

        for (int y = 0; y < this.m_Q[0].length; y++)
        {
            for (int x = 0; x < this.m_Q.length; x++)
            {
                this.m_Q[x][y] = new Qvalues();
            }
        }
    }
    
    
    /**
     * Permet d'obtenir la mémoire interne du robot.
     * 
     * @return tableau de Qvalues du robot.
     */
    public Qvalues[][] getQValues()
    {
    	return this.m_Q; 
    }

    /**
     * Fonction retournant l'image du robot intelligent
     * 
     * @see ca.csf.dsf.poo.tp1.environnement.Robot #getImage()
     * @return I image du robot intelligent
     */
    @Override
    public String getImage()
    {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Redéfinition. Ajout et mise à jour des Q-valeurs.
     * 
     * @see ca.csf.dfc.poo.bots.Robot#addRecompenseAccumulee(int)
     */
    @Override
    public void addAccReward(int p_recompense)
    {
        super.addAccReward(p_recompense);
        Position pNow = this.getPosition();
        Position pOld = pNow.relativePos(Direction.Inverse(m_decisionPrise));

        m_Q[pOld.m_x][pOld.m_y].update(m_decisionPrise, m_Q[pNow.m_x][pNow.m_y], p_recompense);
    }

    /**
     * Décide de quelle direction aller avec certaines probabilités et selon sa table de Q-valeurs (Redéfinition).
     * 
     * @see ca.csf.dfc.poo.bots.Robot#decider(java.util.HashMap)
     */
    @Override
    public Direction decide(HashMap<Direction, Case> p_possibles)
    {
        Direction dirChoisie = null;

        if (p_possibles.size() == 0) return null;

        while (dirChoisie == null)
            switch (m_r.nextInt(4))
            {
                case 0:
                    if (p_possibles.containsKey(Direction.NORD)) dirChoisie = Direction.NORD;
                    break;
                case 1:
                    if (p_possibles.containsKey(Direction.SUD)) dirChoisie = Direction.SUD;
                    break;
                case 2:
                    if (p_possibles.containsKey(Direction.EST)) dirChoisie = Direction.EST;
                    break;
                default:
                    if (p_possibles.containsKey(Direction.OUEST)) dirChoisie = Direction.OUEST;
            }

        HashMap<Direction, Double> probas = new HashMap<Direction, Double>(p_possibles.size());
        Qvalues q = m_Q[this.getPosition().m_x][this.getPosition().m_y];
        double sum = 0;

        for (Direction d : p_possibles.keySet())
        {
            double pr = Math.pow(1.25, q.getValue(d));
            sum += pr;
            probas.put(d, pr);
        }
        for (Direction d : p_possibles.keySet())
            probas.put(d, probas.get(d) / sum);

        double tirage = m_r.nextDouble();
        sum = 0;

        for (Direction d : p_possibles.keySet())
        {
            double pr = probas.get(d);
            sum += pr;
            if (tirage < sum)
            {
                dirChoisie = d;
                break;
            }
        }

        m_decisionPrise = dirChoisie;
        return dirChoisie;
    }

    /**
     * Redéfinition.
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
        return "Intelligent "+super.toString();
    }

}
